package cn.itcast.hotel.service.impl;

import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.pojo.PageResult;
import cn.itcast.hotel.pojo.RequestParams;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
    @Autowired
    private RestHighLevelClient client;
    @Override
    public PageResult search(RequestParams params) {
        try {
            //1、准备Request --指定索引库的名称
            SearchRequest request = new SearchRequest("hoteldemo");
            //2、准备DSL
            //2.1query
            buildBasicQuery(params, request);
            // 2.2、分页 --通过拿到的 page和size 算它的from
            int page = params.getPage();
            int size = params.getSize();
            request.source().from((page-1)*size).size(size);
            //3、发送请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //4、解析响应 -先从 response中 拿到外层的hits,外层的hits中包含“total”、“max_score”、“hits”数组
            return handleResponseHighLight(response);
        } catch (IOException e) {
           throw  new RuntimeException(e);
        } }

    @Override
    public List<String> getSuggestion(String prefix) {

        try {
            //1、准备Request
            SearchRequest hotelRequest = new SearchRequest("hotel");
            //2、准备DSL
            hotelRequest.source().suggest(new SuggestBuilder().addSuggestion(
                    "suggestions", SuggestBuilders.completionSuggestion("suggestion")
                            .prefix(prefix)
                            .skipDuplicates(true)
                            .size(10)
            ));
            //3、发起请求
            SearchResponse response = null;
            response = client.search(hotelRequest, RequestOptions.DEFAULT);
            //4、解析结果
            Suggest suggest = response.getSuggest();
            //4.1、根据补全查询名称，获取补全结果
            CompletionSuggestion suggestions = suggest.getSuggestion("suggestions");
            //4.2、获取options
            List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();
            //4.3、遍历
            ArrayList<String> list = new ArrayList<>(options.size());
            for (CompletionSuggestion.Entry.Option option:options) {
                String s = option.getText().toString();
                list.add(s);
            }
            return list;
        } catch (IOException e) {
            throw new RuntimeException();
        }

    }

    private void buildBasicQuery(RequestParams params, SearchRequest request) {
        //构建BooleanQuery---组合多条件用BooleanQuery
        BoolQueryBuilder boolQueryBuilder=QueryBuilders.boolQuery();
        //之前的关键字搜索应该作为 boolQueryBuilder的must的一部分
        //、--根据关键字搜索 --此时应该是从前端传过来的，即从params中拿出参数--业务上要做健壮性,
        String key = params.getKey();
        if(key==null || "".equals(key)){ //没有条件可以做个全匹配查询
           boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        }else {
            boolQueryBuilder.must(QueryBuilders.matchQuery("all",key)); }
        //城市条件--term查询属于过滤条件，就不要放在must里面，影响得分，影响查询性能
        if(params.getCity()!=null && !params.getCity().equals("")){
            boolQueryBuilder.filter(QueryBuilders.termQuery("city", params.getCity()));
        }
        //品牌条件
        if(params.getBrand()!=null && !params.getBrand().equals("")){
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand", params.getBrand()));
        }
        //星级条件
        if(params.getStarName()!=null && !params.getStarName().equals("")){
            boolQueryBuilder.filter(QueryBuilders.termQuery("starName", params.getStarName()));
        }
        //价格--价格属于范围过滤，最小和最大都不为空，才给做价格过滤
        if(params.getMinPrice()!=null && params.getMaxPrice() !=null){
            boolQueryBuilder.filter(QueryBuilders
                    .rangeQuery("price").gte(params.getMaxPrice()).lte(params.getMinPrice()));
        }
        request.source().query(boolQueryBuilder);
    }

    private PageResult handleResponseHighLight(SearchResponse response) {
        //4、解析响应 -先从 response中 拿到外层的hits,外层的hits中包含“total”、“max_score”、“hits”数组
        SearchHits searchHits = response.getHits();
        long total = searchHits.getTotalHits().value;  //4.1、获取总条数
        SearchHit[] hits = searchHits.getHits();    //4.2、文档数组
        //4.3 遍历文档数组 得到每个hit
        List<HotelDoc> hotels=new ArrayList<>();
        for (SearchHit hit : hits) {
            String json = hit.getSourceAsString(); //获取文档 source
            //把JSON转换成hotelDoc 对象 ，即 反序列化功能
            HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
            hotels.add(hotelDoc);//遍历放入每一条doc
        }
        return new PageResult(total,hotels);//4.4、封装返回
    }
}
